package project.batch;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import project.common.master.Msg;
import batch.base.Batch;
import batch.base.BatchProcessor;
import batch.base.BatchProcessorImpl;

import common.db.JdbcSource;
import common.db.jdbc.Jdbc;

import core.config.Env;
import core.config.Factory;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;

/**
 * バッチ基盤クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class ProjectPerform implements BatchProcessor {

	/** バッチ処理　開始時メッセージコード */
	public static final String MSGID_START = "ZZ999999001";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_END = "ZZ999999002";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_CANCEL = "ZZ999999004";
	/** バッチ処理　終了時メッセージコード */
	public static final String MSGID_ERROR_END = "ZZ999999003";
	/** バッチ処理　引数不正メッセージコード */
	public static final String MSGID_ERROR_ARG = "ZZ999999005";
	/** バッチ処理　バッチステータス不正メッセージコード */
	public static final String MSGID_ERROR_STS = "ZZ999999006";

	/** ログ出力用クラス */
	private static final Logger LOGGER = LogManager.getLogger(ProjectPerform.class);

	/** ログ出力区分 */
	private static final String KUBUN_START = "BatStart";
	/** ログ出力区分 */
	private static final String KUBUN_END = "BatEnd";

	/** バッチ実行 */
	private final BatchProcessorImpl bp;

	/**
	 * コンストラクタ
	 *
	 * @param cls 実行バッチクラス
	 */
	private ProjectPerform(final Class<? extends Batch> cls) {
		this.bp = new BatchProcessorImpl(cls, ProjectParameter.class);
	}

	/**
	 * 開始処理
	 *
	 * @param args 引数
	 * @param cls 実行バッチ
	 * @return 処理結果
	 */
	public static int start(final Class<? extends ProjectBatch> cls, final String... args) {
		return new ProjectPerform(cls).execute(args);
	}

	/**
	 * バッチ用メッセージ読込
	 *
	 * @throws SQLException SQL例外
	 */
	private void setMessage() throws SQLException {
		final String query = String.format("SELECT * FROM %s WHERE %s LIKE ? OR %s = '1'",
				Env.getEnv("Message.Table"), Env.getEnv("Message.MsgId"),
				Env.getEnv("Message.MsgSbt"));

		Msg msg = Factory.create(Msg.class);
		msg.setPropMessage();

		ProjectBatch bat = ProjectBatch.class.cast(this.bp.getBatch());
		String kid = bat.getKinoID();
		if (9 < kid.codePointCount(0, kid.length())) {
			kid = kid.substring(0, kid.offsetByCodePoints(0, 9));
		}

		try (Jdbc conn = JdbcSource.getConnection()) {
			conn.setReadOnly(true);
			conn.setAutoCommit(false);

			try (PreparedStatement psmt = conn.readonlyStatement(query)) {
				psmt.setString(1, "%" + kid + "%");

				try (ResultSet rs = psmt.executeQuery()) {
					while (rs.next()) {
						msg.setHardMessage(rs.getString(Env.getEnv("Message.MsgId")),
								rs.getString(Env.getEnv("Message.MsgTxt")),
								rs.getString(Env.getEnv("Message.MsgSts")));
					}
				}
			}
		}
	}

	/**
	 * 開始前処理
	 *
	 * @param args 引数
	 * @return 処理結果
	 */
	private int execute(final String... args) {

		int exitCode = Batch.RET_SUCCESS;
		try {
			setMessage();

			// 前処理
			exitCode = preprocess(args);
			if (exitCode == Batch.RET_SUCCESS) {
				// 処理実行
				exitCode = process(args);
			}

		} catch (PhysicalException ex) {
			exitCode = Batch.RET_FAILED;
		} catch (SQLException th) {
			ThrowableUtil.error(th);
			exitCode = Batch.RET_FAILED;
		} finally {
			ProjectBatch.class.cast(this.bp.getBatch()).printResult();
			// 終了ログ出力
			LOGGER.fatal(getFinalLog(exitCode));
			exitCode = postprocess(exitCode);
		}

		return exitCode;
	}

	/**
	 * 前処理
	 *
	 * @param params 引数
	 * @return 処理結果
	 */
	@Override
	public int preprocess(final String... params) {
		// 前処理呼び出し
		int ret = this.bp.preprocess(params);
		if (ret == Batch.RET_SUCCESS) {
			// 開始ログ出力
			Msg msg = Factory.create(Msg.class);

			ProjectBatch bat = ProjectBatch.class.cast(this.bp.getBatch());
			StringBuilder sb = bat.getLogHeader();
			sb.append(",").append(KUBUN_START);
			sb.append(",").append(msg.getMessage(MSGID_START, bat.getBatchName()));
			LOGGER.fatal(sb.toString());
		}
		return ret;
	}

	/**
	 * 実処理
	 *
	 * @param args 引数
	 * @return 処理結果
	 */
	@Override
	public int process(final String... args) {
		return this.bp.process(args);
	}

	/**
	 * 後処理
	 *
	 * @param exitCode 処理結果
	 * @return 処理結果
	 */
	@Override
	public int postprocess(final int exitCode) {
		return this.bp.postprocess(exitCode);
	}

	/**
	 * バッチ実行終了ログ取得
	 *
	 * @param exitCode 終了コード
	 * @return 終了ログ
	 */
	private String getFinalLog(final int exitCode) {
		ProjectBatch bat = ProjectBatch.class.cast(this.bp.getBatch());
		StringBuilder sb = bat.getLogHeader().append(",").append(KUBUN_END);
		if (exitCode == Batch.RET_SUCCESS || exitCode == Batch.RET_NODATA
				|| exitCode == Batch.RET_WARNING) {
			Msg msg = Factory.create(Msg.class);
			sb.append(",").append(msg.getMessage(MSGID_END, bat.getBatchName()));
		} else if (exitCode == Batch.RET_CANCELED) {
			Msg msg = Factory.create(Msg.class);
			sb.append(",").append(msg.getMessage(MSGID_CANCEL, bat.getBatchName()));
		} else {
			Msg msg = Factory.create(Msg.class);
			sb.append(",").append(msg.getMessage(MSGID_ERROR_END,
					bat.getBatchName(), String.valueOf(exitCode)));
		}
		return sb.toString();
	}
}
